﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using DevExpress.XtraEditors;
using System.Drawing;
using DevExpress.XtraEditors.Controls;
using System.Windows.Forms;
using DevExpress.Utils.Drawing;
using DevExpress.Utils;
using DevExpress.LookAndFeel;
using DevExpress.Skins;
using System.Windows.Forms.Design;

namespace ProgNetComponentsDx.Controls
{
    [ToolboxItem(true)]
    [Designer(typeof(CollapsiblePanelDesigner))]
    public class CollapsiblePanel : GroupControl
    {
        internal static bool IsLeftOrRight(Locations direction)
        {
            return direction == Locations.Left || direction == Locations.Right;
        }

        #region CTOR
        public CollapsiblePanel()
        {
            this.ShowCaption = true;
            this.BorderStyle = this.DefaultBorderStyle;
            this.autoScrollCore = true;
            this.useViewInfoBoundsAsDisplayRectangle = false;
            this.Scroll += new XtraScrollEventHandler(CollapsiblePanel_Scroll);
        }
        #endregion

        internal static int ScrollBarSize = 18;

        #region Events
        public event EventHandler ToogleCollapse;
        #endregion

        #region Fields
        private bool _AutoScrollWhenExpanded = true;
        protected Size _ExpandedSize = new Size();
        protected Point _ExpandedLoation = new Point();

        protected bool LockExpandedWidth;
        protected bool LockExpandedHeight;
        protected bool LockExpandedX;
        protected bool LockExpandedY;

        private bool OrgAutoScroll { get; set; }
        private Locations OrgCaptionLocation { get; set; }
        private CollapseModes _CollapseMode = CollapseModes.None;
        private bool _Collapsed;
        private Locations _CollapseDirection;
        private string _TextCollapsed;
        #endregion

        #region Properties
        public override DockStyle Dock
        {
            get
            {
                return base.Dock;
            }
            set
            {
                if (Dock != value)
                {
                    base.Dock = value;
                    if (ShouldUpdateCollapseDirection())
                    {
                        switch (value)
                        {
                            case DockStyle.Bottom:
                                CollapseDirection = Locations.Bottom;
                                break;
                            case DockStyle.Left:
                                CollapseDirection = Locations.Left;
                                break;
                            case DockStyle.Right:
                                CollapseDirection = Locations.Right;
                                break;
                            case DockStyle.Top:
                                if (CollapseDirection != Locations.Default && CollapseDirection != Locations.Top)
                                    CollapseDirection = Locations.Top;
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
        }

        protected override BorderStyles DefaultBorderStyle
        {
            get
            {
                return BorderStyles.Simple;
            }
        }
        protected override Size DefaultSize
        {
            get
            {
                return new Size(250, 150);
            }
        }

        protected virtual bool ShouldUpdateCollapseDirection()
        {
            return true;
        }

        public string TextCollapsed
        {
            get
            { return _TextCollapsed; }
            set
            {
                if (value != _TextCollapsed)
                {
                    _TextCollapsed = value;
                    OnChanged();
                }
            }
        }

        [RefreshProperties(System.ComponentModel.RefreshProperties.All)]
        public CollapseModes CollapseMode
        {
            get { return _CollapseMode; }
            set
            {
                _CollapseMode = value;
                OnCollapseModeChanged();
            }
        }

        [RefreshProperties(System.ComponentModel.RefreshProperties.All)]
        public virtual Locations CollapseDirection
        {
            get { return _CollapseDirection; }
            set
            {
                if (_CollapseDirection != value)
                {
                    if (IsAllowedCollapseDirection(value))
                    {
                        _CollapseDirection = value;
                    }
                }
                if (IsAllowedCollapseDirection(value) && Collapsed)
                {
                    SetLocksForCollapsed();
                    CalcExpandedBounds(true);
                    CalcCollapsedBounds();
                }
                OnChanged();
            }
        }

        //[Browsable(false)]
        public Size ExpandedSize
        {
            get { return _ExpandedSize; }
            set
            {
                _ExpandedSize = value;
                if (Collapsed)
                    SetLocksForCollapsed();
            }
        }

        //[Browsable(false)]
        public Point ExpandedLocation
        {
            get { return _ExpandedLoation; }
            set { _ExpandedLoation = value; }
        }

        [Browsable(false)]
        public bool ExpandedAutoSize
        {
            get;
            set;
        }

        [DefaultValue(true)]
        public bool AutoScrollWhenExpanded
        {
            get { return _AutoScrollWhenExpanded; }
            set
            {
                _AutoScrollWhenExpanded = value;
                if (!Collapsed)
                    AutoScroll = value;
            }
        }

        [RefreshProperties(System.ComponentModel.RefreshProperties.All)]
        public bool Collapsed
        {
            get { return _Collapsed; }
            set
            {
                _Collapsed = value;
                UpdateBoundsOnCollapseChanged();
                OnCollapseChanged();
                OnChanged();
            }
        }

        [DefaultValue(true)]
        public override bool ShowCaption
        {
            get
            {
                if (GetCollapseMode() != ProgNetComponentsDx.Controls.CollapseModes.None)
                    return true;
                return base.ShowCaption;
            }
            set
            {
                if (GetCollapseMode() != ProgNetComponentsDx.Controls.CollapseModes.None && !value)
                    return;
                base.ShowCaption = value;
            }
        }

        [Category("Appearance")]
        public new BorderStyles BorderStyle
        {
            get
            {
                if (GetCollapseMode() != CollapseModes.None && base.BorderStyle == BorderStyles.NoBorder)
                    return BorderStyles.Simple;
                return base.BorderStyle;
            }
            set
            {
                if (value == BorderStyles.NoBorder && GetCollapseMode() != CollapseModes.None)
                    return;
                base.BorderStyle = value;
            }
        }
        
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public override bool AutoScroll
        {
            get
            {
                return base.autoScrollCore;
            }
            set
            {
                base.autoScrollCore = value;
                this.PerformLayout();
            }
        }
        #endregion

        #region Override Methods
        protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
        {
            CalcExpandedSize(width, height, specified);
            CalcExpandedLocation(x, y, width, height, specified);
            base.SetBoundsCore(x, y, width, height, specified);
        }

        protected override void Dispose(bool disposing)
        {
            this.Scroll -= new XtraScrollEventHandler(CollapsiblePanel_Scroll);
            base.Dispose(disposing);
        }
        protected override void OnClick(EventArgs e)
        {
            switch (GetCollapseMode())
            {
                case CollapseModes.None:
                    break;
                case CollapseModes.SingleClick:
                case CollapseModes.ButtonClick:
                    CheckClick();
                    break;
                default:
                    break;
            }
            base.OnClick(e);
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            if (GetCollapseMode() == CollapseModes.DoubleClick)
                CheckClick();

            base.OnDoubleClick(e);
        }
        protected override void UpdateViewInfoProperties(GroupObjectInfoArgs info)
        {
            base.UpdateViewInfoProperties(info);
            info.ShowButton = GetCollapseMode() == CollapseModes.ButtonClick;
            info.Expanded = !Collapsed;
            info.Caption = Collapsed ? GetCollapsedText() : Text;
            if (Collapsed)
                info.CaptionLocation = CollapseDirection;
        }
       
        #endregion

        protected virtual void UpdateBoundsOnCollapseChanged()
        {
            if (Collapsed)
            {
                CalcCollapsedBounds();
                AutoScroll = false;
            }
            else
            {
                this.Size = ExpandedSize;
                this.Location = ExpandedLocation;
                AutoScroll = AutoScrollWhenExpanded;
                LockExpandedBounds(false);
            }
        }
        protected virtual void SetLocksForCollapsed()
        {
            LockExpandedSize(false);
            if (IsLeftOrRight(CollapseDirection))
                LockExpandedWidth = true;
            else
                LockExpandedHeight = true;
        }
        protected virtual void OnCollapseChanged()
        {
            if (ToogleCollapse != null)
                ToogleCollapse(this, EventArgs.Empty);
        }
        internal virtual void CalcExpandedBounds(bool fromExpandedBounds)
        {
            Size size = fromExpandedBounds ? ExpandedSize : Size;
            Point location = fromExpandedBounds ? ExpandedLocation : Location;
            CalcExpandedSize(size.Width, size.Height, BoundsSpecified.All);
            CalcExpandedLocation(location.X, location.Y, size.Width, size.Height, BoundsSpecified.All);
        }
        protected virtual void CalcExpandedSize(int width, int height, BoundsSpecified specified)
        {
            if (!LockExpandedHeight && specified.Contains(BoundsSpecified.Height))
                _ExpandedSize.Height = height;

            if (!LockExpandedWidth && specified.Contains(BoundsSpecified.Width))
                _ExpandedSize.Width = width;
        }
        protected virtual void CalcExpandedLocation(int x, int y, int width, int height, BoundsSpecified specified)
        {
            if (!LockExpandedX && specified.Contains(BoundsSpecified.X))
            {
                if (Collapsed && CollapseDirection == Locations.Right)
                    _ExpandedLoation.X = x - _ExpandedSize.Width + width;
                else
                    _ExpandedLoation.X = x;
            }
            if (!LockExpandedY && specified.Contains(BoundsSpecified.Y))
            {
                if (Collapsed && CollapseDirection == Locations.Bottom)
                    _ExpandedLoation.Y = y - _ExpandedSize.Height + height;
                else
                    _ExpandedLoation.Y = y;
            }
        }

        protected virtual bool IsAllowedCollapseDirection(Locations direction)
        {
            switch (Dock)
            {
                case DockStyle.Bottom:
                    return direction == Locations.Bottom;
                case DockStyle.Left:
                    return direction == Locations.Left;
                case DockStyle.Right:
                    return direction == Locations.Right;
                case DockStyle.Top:
                    return direction == Locations.Default || direction == Locations.Top;
                default:
                    return true;
            }
        }
        protected virtual CollapseModes GetCollapseMode()
        {
            return CollapseMode;
        }
        protected virtual void PreCollapseChanged()
        {

        }
        internal virtual void OnCollapseModeChanged()
        {
            if (GetCollapseMode() != ProgNetComponentsDx.Controls.CollapseModes.None)
            {
                if (BorderStyle == BorderStyles.NoBorder)
                    BorderStyle = BorderStyles.Simple;
                ShowCaption = true;    
            }
            OnChanged();
        }
        
        protected virtual string GetCollapsedText()
        {
            if (TextCollapsed.IsNull())
                return Text;
            return TextCollapsed;
        }
        private void CollapsiblePanel_Scroll(object sender, XtraScrollEventArgs e)
        {
            this.Invalidate();
        }

        protected void LockExpandedSize(bool locked)
        {
            LockExpandedHeight = locked;
            LockExpandedWidth = locked;
        }
        protected void LockExpandedLocation(bool locked)
        {
            LockExpandedX = locked;
            LockExpandedY = locked;
        }
        protected void LockExpandedBounds(bool locked)
        {
            LockExpandedLocation(locked);
            LockExpandedSize(locked);
        }

        protected void CalcCollapsedBounds()
        {
            UpdateViewInfoProperties(ViewInfo);
            Painter.CalcObjectBounds(ViewInfo);
            
            LockExpandedBounds(true);
            Size = GetCollapsedSize();
            Location = GetCollapsedLocation();
            SetLocksForCollapsed();
        }

        protected virtual Size GetCollapsedSize()
        {
            int width = ExpandedSize.Width;
            int height = ExpandedSize.Height;
            
            if (IsLeftOrRight(this.CollapseDirection))
                width = ViewInfo.CaptionBounds.Width;
            else
                height = ViewInfo.CaptionBounds.Height;

            return new Size(width, height);
        }
        protected virtual Point GetCollapsedLocation()
        {
            int x = ExpandedLocation.X;
            int y = ExpandedLocation.Y;
            if (CollapseDirection == Locations.Bottom || CollapseDirection == Locations.Right)
            {
                Size collapsedSize = GetCollapsedSize();

                if (CollapseDirection == Locations.Bottom)
                    y += ExpandedSize.Height - collapsedSize.Height;
                else if (CollapseDirection == Locations.Right)
                    x += ExpandedSize.Width - collapsedSize.Width;
            }

            return new Point(x, y);
        }

        private void CheckClick()
        {
            var p = this.PointToClient(Cursor.Position);
            Rectangle r = GetCollapseMode() == CollapseModes.ButtonClick ? ViewInfo.ButtonBounds : ViewInfo.CaptionBounds;
            if (r.Contains(p))
                Collapsed = !Collapsed;
        }
    }

    public enum CollapseModes
    {
        None,
        SingleClick,
        DoubleClick,
        ButtonClick 
    }
    internal class CollapsiblePanelDesigner : ParentControlDesigner
    {
        private CollapsiblePanel Panel
        {
            get
            {
                if (Component is CollapsiblePanel)
                    return (CollapsiblePanel)Component;
                return null;
            }
        }
        public override SelectionRules SelectionRules
        {
            get
            {
                SelectionRules r = base.SelectionRules;

                if (Panel != null && Panel.Collapsed)
                {
                    switch (Panel.CollapseDirection)
                    {
                        case Locations.Bottom:
                        case Locations.Default:
                        case Locations.Top:
                            r &= ~(System.Windows.Forms.Design.SelectionRules.TopSizeable | System.Windows.Forms.Design.SelectionRules.BottomSizeable);
                            break;
                        case Locations.Left:
                        case Locations.Right:
                            r &= ~(System.Windows.Forms.Design.SelectionRules.LeftSizeable | System.Windows.Forms.Design.SelectionRules.RightSizeable);
                            break;
                        default:
                            break;
                    }
                    return r;
                }
                return r;
            }
        }
    }
}
